package com.bix.util.blizfiles.mpq;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;

public class MPQHashTable {
	public	int		HASH_TABLE_SIZE	= 4 + 4 + 2 + 1 + 4;
	
	//
	// The hash of the file path, using method A.
	//
	private	int		filePathHashA;
	
	//
	// The hash of the file path, using method B.
	//
	private	int		filePathHashB;
	
	//
	// The language of the file. This is a Windows LANGID data type, and uses the
	// same values. 0 indicates the default language (American English), or that
	// the file is language-neutral.
	//
	private	short	language;
	
	//
	// The platform the file is used for. 0 indicates the default platform. No
	// other values have been observed.
	//
	private	byte	platform;
	
	//
	// If the hash table entry is valid, this is the index into the block table of
	// the file. Otherwise, one of the following two values:
	// 		FFFFFFFFh:	Hash table entry is empty, and has always been empty.
	// 								Terminates searches for a given file.
	// 		FFFFFFFEh:	Hash table entry is empty, but was valid at some point
	//								(in other words, the file was deleted). Does not terminate
	//								searches for a given file.
	//
	private	int		fileBlockIndex;
	
	public MPQHashTable () {
	}

	public void read (ByteBuffer bb) {
		this.filePathHashA = bb.getInt ();
		this.filePathHashB = bb.getInt ();
		this.language = bb.getShort ();
		this.platform = bb.get();
		this.fileBlockIndex = bb.getInt ();
	}
	
	/**
	 * Reads a hashtable entry from a file channel.
	 * 
	 * @param fc	The file to read from.
	 * 
	 * @return	true if the hashtable was read, false otherwise.
	 * 
	 * @throws	IOException
	 */
	public boolean read (FileChannel fc) throws IOException {
		ByteBuffer	bb = ByteBuffer.allocate(this.HASH_TABLE_SIZE);
		
		//
		// Attempt to read the hash table.
		//
		if (fc.read(bb) < HASH_TABLE_SIZE) {
			return false;
		}
		bb.rewind();
		
		//
		// The hash key for the hash table data is the string "(hash table)".
		//
		int		hash = MPQCrypt.HashString("(hash table)", 0x300);
		
		//
		// Decrypt the hash table.  The decrypt method expects an array of
		// integers.
		//
		IntBuffer	ib = IntBuffer.allocate(HASH_TABLE_SIZE / 4);
		
		
		int[]	ai = ib.array();
		int[] decryptedData = MPQCrypt.DecryptData(ai, hash);
		IntBuffer	decrypted = IntBuffer.wrap(decryptedData);
		
		bb.order(ByteOrder.LITTLE_ENDIAN);
		this.read(bb);
		return true;
	}

	/**
	 * @return the filePathHashA
	 */
	public int getFilePathHashA() {
		return filePathHashA;
	}

	/**
	 * @return the filePathHashB
	 */
	public int getFilePathHashB() {
		return filePathHashB;
	}

	/**
	 * @return the language
	 */
	public short getLanguage() {
		return language;
	}

	/**
	 * @return the platform
	 */
	public byte getPlatform() {
		return platform;
	}

	/**
	 * @return the fileBlockIndex
	 */
	public int getFileBlockIndex() {
		return fileBlockIndex;
	}	
}